package elitequant.event;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Event queue + a thread to dispatch events
 * 
 */
public class LiveEventEngine extends EventsEngine implements Runnable {

	private boolean active;  // if the dispatcher is active

	private LinkedBlockingQueue<Event> queue;  // event queue
	
	private Thread thread;  // dispatcher thread

	private Map<EventType, List<EventHandler>> handlers;  // event handlers list

	private List<EventHandler> generalHandlers;  // handler for all events

	/**
	 * Initialize dispatcher thread and handler function list
	 */
	public LiveEventEngine() {
		active = true;
		queue = new LinkedBlockingQueue<Event>();
		
		thread = new Thread(this);
		handlers = new HashMap<EventType, List<EventHandler>>();
		generalHandlers = new ArrayList<EventHandler>();
	}

	/**
	 * run dispatcher
	 */
	@Override
	public void run() {
		while (active) {
			Event event = null;
			try {
			    event = queue.poll(1, TimeUnit.SECONDS); // timeout: 1 seconds
            } catch (Exception e) {
                System.out.println(String.format("Error: %s", e.getMessage()));
            }
			
			if (event != null) {
				List<EventHandler> targetHandlers = handlers.get(event.getEventType());
				if (targetHandlers != null) {
					for (EventHandler handler : targetHandlers) {
						try {
							handler.handle(event);
						} catch (Exception e) {
							System.out.println(String.format("LiveEventEngine handler Error %s", e.getMessage()));
						}
					}
				}

				for (EventHandler handler : generalHandlers) {
					try {
						handler.handle(event);
					} catch (Exception e) {
						System.out.println(String.format("LiveEventEngine general handler Error %s", e.getMessage()));
					}
				}
			}
		}

	}

	/**
	 * start the dispatcher thread
	 */
	public void start() {
		active = true;
		thread.start();
	}

	public void stop() {
		active = false;
		try {
			thread.join();
		} catch (InterruptedException e) {
			// ignore
		}
	}
	
	@Override
	public void put(Event event) {
		queue.add(event);

	}

	public synchronized void registerHandler(EventType eventType, EventHandler handler) {
		List<EventHandler> targetHandlers = handlers.get(eventType);
		if (targetHandlers == null) {
			targetHandlers = new ArrayList<EventHandler>();
			handlers.put(eventType, targetHandlers);
		}
		targetHandlers.add(handler);

	}

	public synchronized void unregisterHandler(EventType eventType, EventHandler handler) {
		List<EventHandler> targetHandlers = handlers.get(eventType);
		if (targetHandlers != null) {
			targetHandlers.remove(handler);
		}

	}

	public synchronized void registerGeneralHandler(EventHandler handler) {
		generalHandlers.add(handler);

	}

	public synchronized void unregisterGeneralHandler(EventHandler handler) {
		generalHandlers.remove(handler);

	}
}
